x86: irq ratelimit
authorKeir Fraser <keir.fraser@citrix.com>
Wed, 16 Sep 2009 08:16:38 +0000 (09:16 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Wed, 16 Sep 2009 08:16:38 +0000 (09:16 +0100)
This patch adds the feature of irq ratelimit. It temporarily masks
the interrupt (guest) if too many irqs are observed in a short
period (irq storm), to ensure responsiveness of Xen and other guests.

As for now, the threshold can be adjusted at boot time using command-
line option irq_ratelimit=xxx.

Signed-off-by: Qing He <qing.he@intel.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
xen/arch/x86/irq.c
xen/include/xen/irq.h

index eabce3b3e15cc061ac5bcbaa65269dcb62df5a33..620d6b3f5ae8bf1954b54cea0903121b22f6196d 100644 (file)
@@ -55,6 +55,14 @@ DEFINE_PER_CPU(vector_irq_t, vector_irq) = {
 
 DEFINE_PER_CPU(struct cpu_user_regs *, __irq_regs);
 
+static LIST_HEAD(irq_ratelimit_list);
+static DEFINE_SPINLOCK(irq_ratelimit_lock);
+static struct timer irq_ratelimit_timer;
+
+/* irq_ratelimit: the max irq rate allowed in every 10ms, set 0 to disable */
+unsigned int __read_mostly irq_ratelimit_threshold = 10000;
+integer_param("irq_ratelimit", irq_ratelimit_threshold);
+
 /* Must be called when irq disabled */
 void lock_vector_lock(void)
 {
@@ -241,6 +249,7 @@ static void init_one_irq_desc(struct irq_desc *desc)
     desc->msi_desc = NULL;
     spin_lock_init(&desc->lock);
     cpus_setall(desc->affinity);
+    INIT_LIST_HEAD(&desc->rl_link);
 }
 
 static void init_one_irq_status(int irq)
@@ -469,6 +478,32 @@ asmlinkage void do_IRQ(struct cpu_user_regs *regs)
 
     if ( likely(desc->status & IRQ_GUEST) )
     {
+        if ( irq_ratelimit_timer.function && /* irq rate limiting enabled? */
+             unlikely(desc->rl_cnt++ >= irq_ratelimit_threshold) )
+        {
+            s_time_t now = NOW();
+            if ( now < (desc->rl_quantum_start + MILLISECS(10)) )
+            {
+                desc->handler->disable(irq);
+                /*
+                 * If handler->disable doesn't actually mask the interrupt, a 
+                 * disabled irq still can fire. This check also avoids possible 
+                 * deadlocks if ratelimit_timer_fn runs at the same time.
+                 */
+                if ( likely(list_empty(&desc->rl_link)) )
+                {
+                    spin_lock(&irq_ratelimit_lock);
+                    if ( list_empty(&irq_ratelimit_list) )
+                        set_timer(&irq_ratelimit_timer, now + MILLISECS(10));
+                    list_add(&desc->rl_link, &irq_ratelimit_list);
+                    spin_unlock(&irq_ratelimit_lock);
+                }
+                goto out;
+            }
+            desc->rl_cnt = 0;
+            desc->rl_quantum_start = now;
+        }
+
         irq_enter();
         tsc_in = tb_init_done ? get_cycles() : 0;
         __do_IRQ_guest(irq);
@@ -512,6 +547,33 @@ asmlinkage void do_IRQ(struct cpu_user_regs *regs)
     set_irq_regs(old_regs);
 }
 
+static void irq_ratelimit_timer_fn(void *data)
+{
+    struct irq_desc *desc, *tmp;
+    unsigned long flags;
+
+    spin_lock_irqsave(&irq_ratelimit_lock, flags);
+
+    list_for_each_entry_safe ( desc, tmp, &irq_ratelimit_list, rl_link )
+    {
+        spin_lock(&desc->lock);
+        desc->handler->enable(desc->irq);
+        list_del(&desc->rl_link);
+        INIT_LIST_HEAD(&desc->rl_link);
+        spin_unlock(&desc->lock);
+    }
+
+    spin_unlock_irqrestore(&irq_ratelimit_lock, flags);
+}
+
+static int __init irq_ratelimit_init(void)
+{
+    if ( irq_ratelimit_threshold )
+        init_timer(&irq_ratelimit_timer, irq_ratelimit_timer_fn, NULL, 0);
+    return 0;
+}
+__initcall(irq_ratelimit_init);
+
 int request_irq(unsigned int irq,
         void (*handler)(int, void *, struct cpu_user_regs *),
         unsigned long irqflags, const char * devname, void *dev_id)
index 58bf9f05358b388200e2f21feb3a09c5a9d99191..f98756a495d278f0b92b0baa1e066279efac6f3f 100644 (file)
@@ -4,6 +4,7 @@
 #include <xen/config.h>
 #include <xen/cpumask.h>
 #include <xen/spinlock.h>
+#include <xen/time.h>
 #include <asm/regs.h>
 #include <asm/hardirq.h>
 
@@ -74,6 +75,11 @@ typedef struct irq_desc {
     int irq;
     spinlock_t lock;
     cpumask_t affinity;
+
+    /* irq ratelimit */
+    s_time_t rl_quantum_start;
+    unsigned int rl_cnt;
+    struct list_head rl_link;
 } __cacheline_aligned irq_desc_t;
 
 #if defined(__ia64__)